﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Cfg;
using NHibernate.Bytecode;
using System.Reflection;
using NHib = NHibernate;
using NHibernate.Mapping.ByCode;

namespace BeardCoder.Data.Framework.NHibernate
{
    public class Initializer
    {
        #region Public methods

        public static Configuration Initialize(string cfgFile, string[] mappingAssemblies)
        {
            return Initialize(cfgFile, mappingAssemblies, typeof(LazySessionContext).AssemblyQualifiedName);
        }

        public static Configuration InitializeForWindowsService(string cfgFile, string[] mappingAssemblies)
        {
            //return Initialize(cfgFile, mappingAssemblies, typeof(ThreadSessionContext).AssemblyQualifiedName);
            return Initialize(cfgFile, mappingAssemblies);
        }

        private static Configuration Initialize(string cfgFile, string[] mappingAssemblies, string currentSessionContext = "")
        {
            var configuration = new Configuration();
            //read configuration file configuration.
            if (string.IsNullOrEmpty(cfgFile))
                configuration.Configure();
            else
                configuration.Configure(cfgFile);
            //set default proxy for nhibernate.
            configuration.Proxy(p => p.ProxyFactoryFactory<DefaultProxyFactoryFactory>());
            //add mapping assembly.
            foreach (var mappingAssembly in mappingAssemblies)
            {
                var assembly = Assembly.LoadFrom(MakeLoadReadyAssemblyName(mappingAssembly));
                configuration.AddAssembly(assembly);
            }

            //setup lazy session context.
            if (!string.IsNullOrEmpty(currentSessionContext))
                configuration.SetProperty(NHib.Cfg.Environment.CurrentSessionContextClass, currentSessionContext);

            //enable convention mapper.
            var mapper = new ConventionModelMapper();
            mapper.ApplyConventions(configuration);
            var compilingTypes = new List<Type>();
            foreach (var mappingAssembly in mappingAssemblies)
            {
                var assembly = Assembly.LoadFrom(MakeLoadReadyAssemblyName(mappingAssembly));
                mapper.OverrideConventions(assembly);
                compilingTypes.AddRange(assembly.GetExportedTypes().Where(t => t.IsEntity()));
            }
            var hbmMapping = mapper.CompileMappingFor(compilingTypes);
            configuration.AddDeserializedMapping(hbmMapping, "defaultMappings");
            return configuration;
        }

        #endregion

        #region Private methods

        private static string MakeLoadReadyAssemblyName(string assemblyName)
        {
            return (assemblyName.IndexOf(".dll") == -1 && assemblyName.IndexOf(".DLL") == -1 && assemblyName.IndexOf(".Dll") == -1)
                ? assemblyName.Trim() + ".dll"
                : assemblyName.Trim();
        }

        #endregion
    }
}
